package xyz.tanxiao.query;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * QueryWrapper构建器
 *
 * @author 万成波
 * @email 2300064869@qq.com
 * @createTime 2024-09-10 17:44:37 星期二
 */
@Slf4j
public class QueryWrapperBuilder {

	/**
	 * 空值查询条件
	 */
	private static final List<ConditionType> NULL_VALUE_CONDITIONS = Arrays.asList(
		ConditionType.IS_NULL, ConditionType.IS_NOT_NULL
	);

	/**
	 * 范围查询条件
	 */
	private static final List<ConditionType> BETWEEN_CONDITIONS = Arrays.asList(
		ConditionType.BETWEEN, ConditionType.NOT_BETWEEN
	);

	/**
	 * 排序条件
	 */
	private static final List<ConditionType> SORT_CONDITIONS = Arrays.asList(
		ConditionType.ASC_SORT, ConditionType.DESC_SORT
	);



	/**
	 * 构建 MyBatisPlus 的 QueryWrapper，并使用默认的 sort 字段进行 ASC 排序。
	 *
	 * @param obj 查询参数类实例对象
	 * @return QueryWrapper
	 */
	public static <Q, T> QueryWrapper<T> buildAndSortAsc(Q obj) {
		final String sortName = "sort";
		QueryWrapper<T> queryWrapper = build(obj);
		queryWrapper.orderByAsc(sortName);
		return queryWrapper;
	}


	/**
	 * 构建 MyBatisPlus 的 QueryWrapper，并使用默认的 sort 字段进行 DESC 排序。
	 *
	 * @param obj 查询参数类实例对象
	 * @return QueryWrapper
	 */
	public static <Q, T> QueryWrapper<T> buildAndSortDesc(Q obj) {
		final String sortName = "sort";
		QueryWrapper<T> queryWrapper = build(obj);
		queryWrapper.orderByDesc(sortName);
		return queryWrapper;
	}


	/**
	 * 构建 MyBatisPlus 的 QueryWrapper
	 *
	 * @param obj 查询参数类实例对象
	 * @return QueryWrapper
	 */
	public static <Q, T> QueryWrapper<T> build(Q obj) {
		QueryWrapper<T> queryWrapper = new QueryWrapper<>();
		if (Objects.isNull(obj)) {
			return queryWrapper;
		}
		EnableCondition annotation = obj.getClass().getAnnotation(EnableCondition.class);
		if (Objects.isNull(annotation) || !annotation.status()) {
			return queryWrapper;
		}
		return handleCondition(queryWrapper, obj);
	}


	/**
	 * 处理查询属性
	 *
	 * @param wrapper 查询包装器
	 * @param obj 查询类实例对象
	 * @return QueryWrapper
	 */
	private static <T> QueryWrapper<T> handleCondition(QueryWrapper<T> wrapper, Object obj) {
		// 解析查询属性
		List<ConditionProperty> conditions = PropertyUtils.getConditionProperties(obj);

		// 处理查询属性
		List<ConditionProperty> queryConditions = conditions.stream()
			.filter(c -> !SORT_CONDITIONS.contains(c.getType())).collect(Collectors.toList());
		// 构建查询条件
		handleQueryCondition(wrapper, queryConditions);

		// 处理排序属性
		List<ConditionProperty> sortConditions = conditions.stream()
			.filter(c -> SORT_CONDITIONS.contains(c.getType()))
			.sorted(Comparator.comparingInt(ConditionProperty::getSortOrder)).collect(Collectors.toList());
		// 构建排序属性
		handleSortCondition(wrapper, sortConditions);
		return wrapper;
	}


	/**
	 * 处理查询属性
	 *
	 * @param wrapper 查询包装器
	 * @param conditions 查询属性
	 */
	private static <T> void handleQueryCondition(QueryWrapper<T> wrapper, List<ConditionProperty> conditions) {
		for (ConditionProperty condition : conditions) {
			// 跳过范围查询配合属性
			ConditionType type = condition.getType();
			if (BETWEEN_CONDITIONS.contains(type) && condition.isBetweenSlave()) {
				continue;
			}

			// 数据库列名和属性值
			String columnName = condition.getColumnName();
			Object value = condition.getValue();

			// 查询条件需要属性值，但是时属性值为空时，跳过该查询条件。
			boolean nullValue = PropertyUtils.isNullOrEmptyValue(value);
			if (!NULL_VALUE_CONDITIONS.contains(type) && nullValue) {
				continue;
			}

			// 格式化属性值
			value = PropertyUtils.formatValue(value, condition.getFormat());
			log.info("=> 字段名称 {} 字段值 {} 查询条件 {}", columnName, value, type);

			// 处理查询条件
			switch (type) {
				// 等于
				case EQ : wrapper.eq(columnName, value);break;

				// 不等于
				case NE : wrapper.ne(columnName, value);break;

				// 大于
				case GT : wrapper.gt(columnName, value);break;

				// 大于等于
				case GE : wrapper.ge(columnName, value);break;

				// 小于
				case LT : wrapper.lt(columnName, value);break;

				// 小于等于
				case LE : wrapper.le(columnName, value);break;

				// 区间范围
				case BETWEEN : {}

				// 区间范围, 非区间范围
				case NOT_BETWEEN : {
					String betweenSql = buildBetweenSql(condition, value, conditions);
					wrapper.apply(betweenSql);
					break;
				}

				// 全模糊
				case LIKE : wrapper.like(columnName, value);break;

				// 左模糊
				case LEFT_LIKE : wrapper.likeLeft(columnName, value);break;

				// 右模糊
				case RIGHT_LIKE : wrapper.likeRight(columnName, value);break;

				// 非全模糊
				case NOT_LIKE : wrapper.notLike(columnName, value);break;

				// 非左模糊
				case NOT_LEFT_LIKE : wrapper.notLikeLeft(columnName, value);break;

				// 非右模糊
				case NOT_RIGHT_LIKE : wrapper.notLikeRight(columnName, value);

					// 为空
				case IS_NULL : wrapper.isNull(columnName);break;

				// 不为空
				case IS_NOT_NULL : wrapper.isNotNull(columnName);break;

				// 包含
				case IN : {
					Collection<?> values = PropertyUtils.getCollectionValues(value);
					wrapper.in(columnName, values);
					break;
				}

				// 不包含
				case NOT_IN : {
					Collection<?> values = PropertyUtils.getCollectionValues(value);
					wrapper.notIn(columnName, values);
					break;
				}
			}
		}
	}


	/**
	 * 处理排序属性
	 *
	 * @param wrapper 查询包装器
	 * @param conditions 排序属性
	 */
	private static <T> void handleSortCondition(QueryWrapper<T> wrapper, List<ConditionProperty> conditions) {
		for (ConditionProperty condition : conditions) {
			String columnName = condition.getColumnName();
			ConditionType type = condition.getType();
			log.info("=> 字段名称 {} 排序条件 {}", columnName, type);

			if (type == ConditionType.ASC_SORT) {
				wrapper.orderByAsc(condition.getPropertyName());
			}
			else if (type == ConditionType.DESC_SORT) {
				wrapper.orderByDesc(condition.getPropertyName());
			}
		}
	}


	/**
	 * 构建区间查询SQL语句
	 *
	 * @param condition 条件属性
	 * @param masterValue 属性值
	 * @param conditions 所有条件属性
	 * @return 区间查询SQL语句
	 */
	private static String buildBetweenSql(ConditionProperty condition, Object masterValue, List<ConditionProperty> conditions) {
		// 数据库字段日期时间格式
		FormatMappingHelper.FormatType formatType = FormatMappingHelper.getFormatType(DbType.MYSQL);
		String dbFormat = formatType.getDbFormat(condition.getFormat());

		// 构建字段格式化SQL段落
		String formatSql = "";
		String columnName = condition.getColumnName();
		if (StringUtils.isNotBlank(dbFormat)) {
			formatSql = String.format(formatType.getFormatMethod(), columnName, dbFormat);
		}

		// 匹配范围查询的另外一个配合属性
		ConditionProperty betweenSlave = null;
		if (StringUtils.isNotBlank(condition.getBetweenGroup())) {
			betweenSlave = conditions.stream()
				.filter(c -> BETWEEN_CONDITIONS.contains(c.getType()) && c.isBetweenSlave()
						&& condition.getBetweenGroup().equals(c.getBetweenGroup()))
				.findFirst().orElse(null);
		}

		// 设置配合属性的属性值
		ConditionType type = condition.getType();
		Object slaveValue;
		if (Objects.nonNull(betweenSlave)) {
			slaveValue = PropertyUtils.formatValue(betweenSlave.getValue(), betweenSlave.getFormat());
			log.info("=> 字段 {} 匹配到 {} 查询的配合属性 {}", columnName, type, betweenSlave.getColumnName());
		} else {
			slaveValue = masterValue;
			log.warn("=> 字段 {} 未匹配到 {} 查询的配合属性", columnName, type);
		}

		// 将范围查询条件名称下划线替换为空格
		String conditionName = type.name().replace("_", " ");

		// 构建范围查询SQL段落
		String betweenSql = String.format("%s '%s' AND '%s'", conditionName, masterValue, slaveValue);
		if (StringUtils.isBlank(formatSql)) {
			formatSql = columnName;
		}

		betweenSql = String.format("%s %s", formatSql, betweenSql);
		log.info("=> 范围查询 SQL 段落 {}", betweenSql);
		return betweenSql;
	}

}
